import java.awt.Point;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;

public class Tools
{
    public static boolean CheckHeapSize()
    { // returns false if heap usage is dangerously high

        // https://stackoverflow.com/questions/2015463/how-to-view-the-current-heap-size-that-an-application-is-using

        long HeapSize = Runtime.getRuntime().totalMemory();
        long HeapSizeMax = Runtime.getRuntime().maxMemory();

        // System.out.println(HeapSize + " " + HeapSizeMax);

        if (HeapSize > 4L * 1024L * 1024L * 1024L - 500L * 1024L * 1024L) // leave 500 MB
            return false;
        if (HeapSize >= HeapSizeMax - 500L * 1024L * 1024L) // leave 500 MB
            return false;

        return true;
    }

    public static String PromptLoadFile(String DialogTitle, String DefaultDirectory)
    { // returns absolute path to file selected by user or "" if the user cancelled

        JFileChooser Dialog = new JFileChooser(); // https://www.codejava.net/java-se/swing/show-save-file-dialog-using-jfilechooser

        Dialog.setDialogTitle(DialogTitle);
        Dialog.setCurrentDirectory(new File(DefaultDirectory));

        int DialogResult = Dialog.showOpenDialog(null);

        if (DialogResult == JFileChooser.APPROVE_OPTION)
        {
            return Dialog.getSelectedFile().getAbsolutePath();
        }
        else
        {
            return "";
        }
    }

    public static String[] ReadFileLines(String FilePath)
    { // https://stackoverflow.com/questions/285712/java-reading-a-file-into-an-array, heavily altered

        try (BufferedReader BufferedReader = new BufferedReader(new FileReader(FilePath)))
        {
            String Line = null;
            ArrayList<String> Lines = new ArrayList<String>();

            while ((Line = BufferedReader.readLine()) != null)
            {
                while (true)
                { // by me:
                    int LineLengthOld = Line.length();
                    Line = Line.replace("\r", "").replace("\n", ""); // make sure there's absolutely no newline in returned lines
                    if (Line.length() == LineLengthOld)
                        break;
                }
                if (Line != null && Line.length() >= 1) // by me: skip empty lines
                {
                    Lines.add(Line);
                }
            }

            BufferedReader.close();

            return (Lines.toArray(new String[Lines.size()]));
        }
        catch (Exception e)
        {
            System.out.println("error in Tools.ReadFileLines(): " + e.getMessage());

            return null;
        }
    }

    // WriteUTF32Bit()/ReadUTF32Bit() in the present states have been tested with
    // "aaaaaaaaaa..." strings of ALL lengths from 0 to 100000, the read strings
    // were always identical to the written ones (code seems to be reliable).

    private final static int UTF32BitContinue = ((int) '3' << 24) + ((int) '2' << 16) + ((int) 'b' << 8) + ((int) 'c');
    private final static int UTF32BitEnd = ((int) '3' << 24) + ((int) '2' << 16) + ((int) 'b' << 8) + ((int) 'e');

    public static String ReadUTF32Bit(DataInputStream is) throws IOException
    {
        String S = ""; // don't think StringBuilder creation and .toString() overhead is worth it (mostly we do one while (true)-iteration only)

        while (true)
        {
            int Marking = is.readInt();
            if (Marking == UTF32BitContinue)
                S = S + is.readUTF();
            else if (Marking == UTF32BitEnd) // it might be that there's ONLY one end marking in file, no continue marking and no actual string!
                break;
            else
                throw new IOException("ReadUTF32Bit(): format screwed");
        }

        return S;
    }

    public static String ShowInputDialog(String Message)
    {
        return JOptionPane.showInputDialog(Message);
    }

    public static String ShowInputDialog(String Message, String DefaultText)
    {
        return JOptionPane.showInputDialog(Message, DefaultText);
    }

    public static void ShowMessageDialog(MainWindow ParentComponentOrNull, String Message)
    {
        if (!(Message.endsWith(".") || Message.endsWith("...") || Message.endsWith("!") || Message.endsWith("?")))
            if (!(Message.endsWith(" ") || Message.endsWith("\t")))
                Message = Message + ".";

        JOptionPane.showMessageDialog(ParentComponentOrNull, Message.trim());
    }

    public static boolean ShowYesNoDialog(MainWindow ParentComponentOrNull, String Message)
    {
        if (!(Message.endsWith(".") || Message.endsWith("...") || Message.endsWith("!") || Message.endsWith("?")))
            if (!(Message.endsWith(" ") || Message.endsWith("\t")))
                Message = Message + ".";

        int DialogResult = JOptionPane.showConfirmDialog(ParentComponentOrNull, Message.trim(), "Question", JOptionPane.YES_NO_OPTION);

        return (DialogResult == JOptionPane.YES_OPTION);
    }

    public static Point SubtractPoints(Point p1, Point p2)
    {
        return new Point(p1.x - p2.x, p1.y - p2.y);
    }

    public static void WriteUTF32Bit(DataOutputStream os, String S) throws IOException
    {
        // Java's .writeUTF() can only save 16-bit string lengths, so we NEED to implement
        // our own string writing/reading (sites on the Internet did recommend this, too)

        int StartIndex = 0;
        int EndIndex = 10000;

        while (true)
        {
            if (StartIndex < S.length()) // don't write ""
            {
                os.writeInt(UTF32BitContinue);
                os.writeUTF(S.substring(StartIndex, Math.min(S.length(), EndIndex))); // if writing "", end marking is not written correctly (just 32-bit 0), don't know why

                StartIndex += 10000;
                EndIndex += 10000;
            }
            else
            {
                break;
            }
        }
        os.writeInt(UTF32BitEnd);
    }
}
